package org.oims.simpleserver.services.impl;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.oims.simpleserver.endpoint.Endpoint;
import org.oims.simpleserver.endpoint.EndpointArbiter;
import org.oims.simpleserver.entities.FriendChatDetailItem;
import org.oims.simpleserver.entities.db.FriendChatListItem;
import org.oims.simpleserver.entities.db.FriendMsgItem;
import org.oims.simpleserver.entities.db.User;
import org.oims.simpleserver.entities.msg.DataMsg;
import org.oims.simpleserver.entities.msg.FriendMsgData;
import org.oims.simpleserver.entities.msg.SimpleMsg;
import org.oims.simpleserver.exceptions.ClientOfflineException;
import org.oims.simpleserver.exceptions.IllegalOperationException;
import org.oims.simpleserver.mapper.MessageMapper;
import org.oims.simpleserver.mapper.UserMapper;
import org.oims.simpleserver.msg.MsgNotifier;
import org.oims.simpleserver.services.UserService;
import org.oims.simpleserver.tasks.TaskQueueExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class UserServiceImpl implements UserService {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    private final UserMapper userMapper;
    private final MessageMapper messageMapper;
    private final EndpointArbiter endpointArbiter;
    private Map<String, String> tokenMpNoMap = new HashMap<>();
    private Map<String, String> MpNoTokenMap = new HashMap<>();
    private Lock tokenMpNoLock = new ReentrantLock();

    @Autowired
    public UserServiceImpl(UserMapper userMapper, MessageMapper messageMapper, EndpointArbiter endpointArbiter){
        this.userMapper = userMapper;
        this.messageMapper = messageMapper;
        this.endpointArbiter = endpointArbiter;
    }

    @Override
    public boolean isCorrectUserPwd(String MpNo, String testedPwd) {
        if(StringUtils.isEmpty(MpNo) || StringUtils.isEmpty(testedPwd))
            return false;
        String encryptedPwd = userMapper.getUserEncryptedPwd(MpNo);
        return StringUtils.equals(testedPwd, encryptedPwd);
    }

    @Override
    public List<FriendChatDetailItem> getFriendChatDetails(String MpNo) {
        return userMapper.getFriendChatDetails(MpNo);
    }

    @Override
    public User login(String MpNo, String pwd) {
        User user = userMapper.getUser(MpNo, pwd);
        if(user != null){
            Calendar now = Calendar.getInstance();
            userMapper.updateLastLoginTime(MpNo, now);
        }
        return user;
    }

    @Override
    public List<FriendMsgItem> getLstFriendMsgsAndUpdateStatus(String userMpNo, String friendMpNo, int msgNum) {
        List<FriendMsgItem> list = userMapper.getLatestFriendMsgs(userMpNo, friendMpNo, msgNum);
        //找到第一个未接收的好友消息，并更新未接收消息数为0
        if (list.size() > 0) {
            FriendMsgItem firstMsg = list.get(0);
            if (firstMsg.getStatus() == 0 && StringUtils.equals(firstMsg.getReception(), userMpNo)) {
                userMapper.setZeroNumForFriChatList(userMpNo, friendMpNo);
            }
        }

        for (FriendMsgItem item : list) {
            if (item.getStatus() == 0 && StringUtils.equals(item.getReception(), userMpNo)) {
                userMapper.updateFriendMsgStatus(item.getId(), 1);
            } else {
                break;
            }
        }

        return list;
    }

    @Override
    public FriendMsgItem sendMsgToFriend(String myMpNo, String friendMpNo, int type, String content) {
        Calendar now = Calendar.getInstance();
        //保存好友消息
        FriendMsgItem friendMsg = new FriendMsgItem(0, myMpNo, friendMpNo, type, 0, content, now);
        int rowNum = messageMapper.insertFriendMsg(friendMsg);
        if (rowNum != 1) {
            throw new IllegalOperationException("‘" + friendMpNo + "’ => ‘" + myMpNo + "’ is not friend relation.");
        }
        //更新好友聊天列表项
        rowNum = userMapper.updateFCLtCTimeAndUnRum(friendMpNo, myMpNo, now);
        if (rowNum <= 0) {
            userMapper.insertFriendChatList(new FriendChatListItem(friendMpNo, myMpNo, 0, now, 1));
        }
        //更新发送信息用户自己的聊天列表项的创建时间
        userMapper.updateFriendChatListCTime(myMpNo, friendMpNo, now);

        //异步推送好友发过来的消息
        TaskQueueExecutor.addTask(() -> {
            try{
                //topic向客户端推送推送消息
                Object data = new FriendMsgData(myMpNo, now);
                SimpleMsg msg = new DataMsg("friend-msg", data);
                MsgNotifier.sendMsg(friendMpNo, msg);
            }catch (ClientOfflineException e){
                logger.info(e.getMessage());
            }catch (Exception e){
                logger.error(e.getMessage());
                e.printStackTrace();
            }
        });
        return friendMsg;
    }

    @Override
    public void registerEndpoint(String token, Endpoint endpoint) {
        tokenMpNoLock.lock();
        String MpNo;
        try {
            MpNo = tokenMpNoMap.get(token);
            if(MpNo != null){
                MpNoTokenMap.remove(MpNo);
                tokenMpNoMap.remove(token);
            }
        }finally {
            tokenMpNoLock.unlock();
        }
        if(MpNo != null){
            endpointArbiter.register(MpNo, endpoint);
        }
    }

    @Override
    public String getToken(String MpNo) {
        tokenMpNoLock.lock();
        try {
            String token = MpNoTokenMap.get(MpNo);
            if(token != null){
                tokenMpNoMap.remove(token);
            }
            token = RandomStringUtils.randomAlphabetic(25);
            MpNoTokenMap.put(MpNo, token);
            tokenMpNoMap.put(token, MpNo);

            return token;
        }finally {
            tokenMpNoLock.unlock();
        }
    }

    @Override
    public void removeTokenByMpNo(String MpNo) {
        tokenMpNoLock.lock();
        try {
            String token = MpNoTokenMap.get(MpNo);
            if(token != null){
                MpNoTokenMap.remove(MpNo);
                tokenMpNoMap.remove(token);
            }
        }finally {
            tokenMpNoLock.unlock();
        }
        endpointArbiter.unregister(MpNo);
    }

    @Override
    public void closeEndpoint(String MpNo, Endpoint endpoint) {
        endpointArbiter.unregister(MpNo, endpoint);
    }


}
